using System;
using BeeDevelopment.Brazil;

namespace Stetson.Emulator {
	public partial class TI83Plus : Z80A {

		#region Properties

		private bool onKeyPressed;
		/// <summary>
		/// Gets or sets whether the On key is pressed.
		/// </summary>
		public bool OnKeyPressed {
			get { return this.onKeyPressed; }
			set {
				this.onKeyPressed = value;
				this.interruptGeneratedByOnKey |= (this.onKeyPressed && this.interruptMaskOnKey);
			}
		}

		private bool interruptMaskOnKey = true;
		/// <summary>
		/// Gets or sets whether the On key can generate interrupts.
		/// </summary>
		public bool InterruptMaskOnKey {
			get { return this.interruptMaskOnKey; }
			set { this.interruptMaskOnKey = value; }
		}

		private bool interruptMaskTimer1 = true;
		/// <summary>
		/// Gets or sets whether the first hardware timer can generate interrupts.
		/// </summary>
		public bool InterruptMaskTimer1 {
			get { return this.interruptMaskTimer1; }
			set { this.interruptMaskTimer1 = value; }
		}

		private bool interruptMaskTimer2 = true;
		/// <summary>
		/// Gets or sets whether the second hardware timer can generate interrupts.
		/// </summary>
		public bool InterruptMaskTimer2 {
			get { return this.interruptMaskTimer2; }
			set { this.interruptMaskTimer2 = value; }
		}

		private bool interruptMaskLinkPort = true;
		/// <summary>
		/// Gets or sets whether the link port can generate interrupts.
		/// </summary>
		public bool InterruptMaskLinkPort {
			get { return this.interruptMaskLinkPort; }
			set { this.interruptMaskLinkPort = value; }
		}

		private bool interruptGeneratedByOnKey;
		/// <summary>
		/// Gets or sets whether the On key just generated an interrupt.
		/// </summary>
		public bool InterruptGeneratedByOnKey {
			get { return this.interruptGeneratedByOnKey; }
			set { this.interruptGeneratedByOnKey = value; }
		}

		private bool interruptGeneratedByTimer1;
		/// <summary>
		/// Gets or sets whether the first hardware timer just generated an interrupt.
		/// </summary>
		public bool InterruptGeneratedByTimer1 {
			get { return this.interruptGeneratedByTimer1; }
			set { this.interruptGeneratedByTimer1 = value; }
		}

		private bool interruptGeneratedByTimer2;
		/// <summary>
		/// Gets or sets whether the second hardware timer just generated an interrupt.
		/// </summary>
		public bool InterruptGeneratedByTimer2 {
			get { return this.interruptGeneratedByTimer2; }
			set { this.interruptGeneratedByTimer2 = value; }
		}

		private bool interruptGeneratedByLinkPort;
		/// <summary>
		/// Gets or sets whether the link port just generated an interrupt.
		/// </summary>
		public bool InterruptGeneratedByLinkPort {
			get { return this.interruptGeneratedByLinkPort; }
			set { this.interruptGeneratedByLinkPort = value; this.UpdateInterruptPin(); }
		}

		private int timerFrequency;
		/// <summary>
		/// Gets or sets the timer interrupt frequency (0-3).
		/// </summary>
		public int TimerFrequency {
			get { return this.timerFrequency; }
			set {
				if (value < 0 || value > 3) throw new ArgumentOutOfRangeException("Timer frequency must be between 0 and 3.");
				this.timerFrequency = value;
			}
		}

		#endregion

		#region Methods

		// Read port 0x03.
		private byte ReadInterruptMask() {
			return (byte)(
				(this.interruptMaskOnKey ? 0x01 : 0x00) |
				(this.interruptMaskTimer1 ? 0x02 : 0x00) |
				(this.interruptMaskTimer2 ? 0x04 : 0x00) |
				(this.interruptMaskLinkPort ? 0x10 : 0x00)
			);
		}

		// Read port 0x04.
		private byte ReadInterruptDeviceIndentification() {
			return (byte)(
				(this.interruptGeneratedByOnKey ? 0x01 : 0x00) |
				(this.interruptGeneratedByTimer1 ? 0x02 : 0x00) |
				(this.interruptGeneratedByTimer2 ? 0x04 : 0x00) |
				(this.onKeyPressed ? 0x00 : 0x08) |
				(this.interruptGeneratedByLinkPort ? 0x10 : 0x00)
			);
		}

		// Write port 0x03. 
		private void WriteInterruptMask(byte value) {
			this.interruptMaskOnKey = (value & 0x01) != 0;
			this.interruptMaskTimer1 = (value & 0x02) != 0;
			this.interruptMaskTimer2 = (value & 0x04) != 0;
			this.interruptMaskLinkPort = (value & 0x10) != 0;

			this.interruptGeneratedByOnKey &= this.interruptMaskOnKey;
			this.interruptGeneratedByTimer1 &= this.interruptMaskTimer1;
			this.interruptGeneratedByTimer2 &= this.interruptMaskTimer2;
			this.interruptGeneratedByLinkPort &= this.interruptMaskLinkPort;
			
			this.UpdateInterruptPin();
		}

		private void UpdateInterruptPin() {
			this.Interrupt = this.interruptGeneratedByOnKey || this.interruptGeneratedByTimer1 || this.interruptGeneratedByTimer2 || this.interruptGeneratedByLinkPort;
		}

		private void RunTimerInterrupts() {
		
			if (!this.interruptGeneratedByTimer1 && --this.Timer1Pending < 0) {
				this.Timer1Pending += 64 + 80 * this.TimerFrequency;
				if (this.interruptMaskTimer1) {
					this.interruptGeneratedByTimer1 = true;
				}
			}
			if (!this.interruptGeneratedByTimer2 && --this.Timer2Pending < 0) {
				this.Timer2Pending += (64 + 80 * this.TimerFrequency) / 2;
				if (this.interruptMaskTimer2) {
					this.interruptGeneratedByTimer2 = true;
				}
			}

			this.UpdateInterruptPin();
		}

		#endregion



	}
}
